# coding:UTF-8
import json
import os
import random
import shutil
import signal
import sys
import time
from datetime import datetime

import paho.mqtt.client as mqtt
import taosrest
from iniconfig import IniConfig


class SailboatMonitor:

    def __init__(self, tenant_code: str, client_id: str, mqtt_host: str, port: int, clean_session: bool,
                 mqtt_username: str, mqtt_password: str, keepalive: int, topic_list: list[tuple[str, int]],
                 db_url: str, db_user: str, db_password: str, delete_log_path: bool):
        self.tenant_code = tenant_code
        self.client_id = client_id
        self.mqtt_host = mqtt_host
        self.port = port
        self.clean_session = clean_session
        self.mqtt_username = mqtt_username
        self.mqtt_password = mqtt_password
        self.keepalive = keepalive
        self.topic_list = topic_list
        # self.conn = taosrest.connect(url=db_url, user=db_user, password=db_password)
        self.delete_log_path = delete_log_path

    def initPath(self):
        if self.delete_log_path:
            folder_path = "log"
            # 检查文件夹是否存在
            if os.path.exists(folder_path) and os.path.isdir(folder_path):
                # 删除文件夹及其所有内容
                shutil.rmtree(folder_path)
                print(f"文件夹 {folder_path} 已删除")
            else:
                print(f"文件夹 {folder_path} 不存在")

    def initMqtt(self, level_info_dict: dict, user_info_dict: dict):

        def handler(topic: str, payload: bytes):
            payload_str = payload.decode()
            splits = topic.split("/")
            file_name = None
            file_path = None
            if splits[0] == "gateway":
                file_name = f"{splits[4]}.txt"
                file_path = f"log/env"
            elif splits[0] == "pad":
                if splits[2] == "record":
                    data_type = "pad_" + splits[2]
                    file_path = f"log/{level_info_dict.get(splits[4], "-")}/{user_info_dict.get(splits[5], "-")}/record"
                    file_name = f"{data_type}.txt"
            elif splits[0] == "mobile":
                if splits[2] == "general-train":
                    data_type = splits[2]
                    level_id = splits[4]
                    m_hm_id = splits[5]
                    root = json.loads(payload)
                    data = root.get("data", {})
                    # self.__insert_data_2(payload_str, level_id, m_hm_id)

                    # ts,mHmName,color,longitude,latitude,headingT,headingM,sailStatus,leeway,speed,standardSpeed,customStandardSpeed,angle,vmg,standardVmg,customAngle,customVmg,customStandardVmg,windSourceBoatCode,windSpeed,windDirection,customWindSpeed,customWindDirection,yaw,pitch,rol,posture,pitchPosture,hr
                    payload_str = (f"{root.get('ts', "")},"
                                   f"{data.get('mHmName', "")},{data.get('color', "")},"
                                   f"{data.get('longitude', "")},{data.get('latitude', "")},"
                                   f"{data.get('headingT', "")},{data.get('headingM', "")},{data.get('sailStatus', "")},{data.get('leeway', "")},"
                                   f"{data.get('speed', "")},{data.get('standardSpeed', "")},{data.get('customStandardSpeed', "")},"
                                   f"{data.get('angle', "")},{data.get('vmg', "")},{data.get('standardVmg', "")},"
                                   f"{data.get('customAngle', "")},{data.get('customVmg', "")},{data.get('customStandardVmg', "")},"
                                   f"{data.get('windSourceBoatCode', "")},{data.get('windSpeed', "")},{data.get('windDirection', "")},{data.get('customWindSpeed', "")},{data.get('customWindDirection', "")},"
                                   f"{data.get('yaw', "")},{data.get('pitch', "")},{data.get('rol', "")},{data.get('posture', "")},{data.get('pitchPosture', "")},"
                                   f"{data.get('hr', "")}")
                    file_path = f"log/{level_info_dict.get(level_id, "-")}/{user_info_dict.get(m_hm_id, "-")}/compute"
                elif splits[2] == "route-train":
                    root = json.loads(payload)
                    data = root.get("data", {})
                    payload_str = (f"{data.get('qh', "")},{data.get('ttsl', "")},{data.get('dtsl', "")},"
                                   f"{data.get('td', "")},{data.get('cp', "")},{data.get('l', "")},{data.get('gl', "")},"
                                   f"{data.get('curLap', "")},{data.get('cumLaps', "")},{data.get('trainLaps', "")}\n")
                    data_type = splits[2]
                    level_id = splits[4]
                    m_hm_id = splits[5]
                    file_path = f"log/{level_info_dict.get(level_id, "-")}/{user_info_dict.get(m_hm_id, "-")}/train"
                elif splits[2] == "original":
                    data_type = json.loads(payload)['dataType']
                    level_id = splits[4]
                    m_hm_id = splits[5]
                    file_path = f"log/{level_info_dict.get(level_id, "-")}/{user_info_dict.get(m_hm_id, "-")}/original"
                else:
                    data_type = "mobile_" + splits[2]
                    level_id = splits[4]
                    m_hm_id = splits[5]
                    file_path = f"log/{level_info_dict.get(level_id, "-")}/{user_info_dict.get(m_hm_id, "-")}/record"
                file_name = f"{data_type}.txt"
            elif splits[0] == "gw":
                data = json.loads(payload)
                if splits[2] == "connected":
                    payload_str = data.get("clientId", "")
                else:
                    payload_str = f"{data.get("clientId", "")},{data.get("reason", "")}"

                file_name = f"{splits[2]}.txt"
                file_path = f"log/client"

            if file_name and file_path:
                if not os.path.exists(file_path):
                    os.makedirs(file_path, exist_ok=True)
                with open(f"{file_path}/{file_name}", "a", encoding="utf-8") as file:
                    file.write(f"{payload_str}\n")
            else:
                print(f"{time.strftime("%Y-%m-%d %H:%M:%S")} {topic} 无法写入文件 {file_path} {file_name}")

        def on_connect(client, userdata, flags, rc):
            if rc == 0:
                print(f"【{self.mqtt_host} {self.port}】mqtt客户端连接成功")
                # 订阅主题
                client.subscribe(self.topic_list)
            else:
                print(f"【{self.mqtt_host} {self.port}】mqtt客户端连接失败 {rc}")

        def on_message(client, userdata, msg):
            handler(msg.topic, msg.payload)

        def on_subscribe(client, userdata, mid, granted_qos):
            print(f"【{self.mqtt_host} {self.port}】mqtt客户端订阅成功 {mid} {granted_qos}")

        def on_disconnect(client, userdata, rc):
            print(f"【{self.mqtt_host} {self.port}】mqtt客户端断开连接 {rc}")

        mqtt_client = mqtt.Client(client_id=self.client_id, clean_session=self.clean_session)
        mqtt_client.username_pw_set(self.mqtt_username, self.mqtt_password)

        mqtt_client.on_connect = on_connect
        mqtt_client.on_message = on_message
        mqtt_client.on_subscribe = on_subscribe
        mqtt_client.on_disconnect = on_disconnect
        mqtt_client.connect(self.mqtt_host, self.port, self.keepalive)

        def signal_handler(signum, frame):
            print(f"收到信号 SIGINT={signum}，正在关闭 MQTT 客户端...")
            mqtt_client.loop_stop()
            mqtt_client.disconnect()
            sys.exit(0)

        signal.signal(signal.SIGINT, signal_handler)

        # 保持网络循环，等待消息
        mqtt_client.loop_forever()

    def __insert_data_2(self, data: str, level_id: str, m_hm_id: str):
        if self.conn is None:
            return
        root = json.loads(data)
        data = root.get("data", {})
        sql_prefix = f"insert into mobile.compute_data_v2_{datetime.now().strftime('%Y%m%d')}_{level_id}_{m_hm_id} using mobile.compute_data_v2 TAGS ('{self.tenant_code}', '{level_id}', '{m_hm_id}') "
        now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        sql_suffix = (
            f"(ts, longitude, latitude, heading_t, heading_m, sail_status, leeway, speed, standard_speed, custom_standard_speed, angle, vmg, standard_vmg, custom_angle, custom_vmg, custom_standard_vmg, wind_source_boat_code, wind_speed, wind_direction, custom_wind_speed, custom_wind_direction, yaw, pitch, rol, posture, pitchPosture, hr) values "
            f"('{root.get('ts', "")}', '{data.get('longitude', "")}', '{data.get('latitude', "")}', '{data.get('headingT', "")}', '{data.get('headingM', "")}', '{data.get('sailStatus', "")}', "
            f"'{data.get('leeway', "")}', '{data.get('speed', '')}', '{data.get('standardSpeed', '')}', '{data.get('customStandardSpeed', '')}', '{data.get('angle', '')}', "
            f"'{data.get('vmg', '')}', '{data.get('standardVmg', '')}', '{data.get('customAngle', '')}', '{data.get('customVmg', '')}', '{data.get('customStandardVmg', '')}', "
            f"'{data.get('windSourceBoatCode', '')}', '{data.get('windSpeed', '')}', '{data.get('windDirection', '')}', '{data.get('customWindSpeed', '')}', '{data.get('customWindDirection', '')}', '{data.get('yaw', '')}', '{data.get('pitch', '')}', '{data.get('rol', '')}', '{data.get('posture', '')}', '{data.get('pitchPosture', '')}', '{data.get('hr', '')}')")

        # 数据入库
        if sql_suffix:
            sql = sql_prefix + sql_suffix
            print(f"{now_str}->{sql}")
            try:
                self.conn.execute(sql)
            except Exception as e:
                print(f"入库失败 {e}")


if __name__ == "__main__":
    ini = IniConfig('config_dev.ini')

    user_info: dict[str, str] = {}
    for key in ini['sys.user_info']:
        user_info[key] = ini.get('sys.user_info', key)

    level_info: dict[str, str] = {}
    for key in ini['sys.level_info']:
        level_info[key] = ini.get('sys.level_info', key)

    client_id = f"py_sailboat_{random.randint(1000, 9999)}"
    mqtt_host = ini['mqtt']['host']
    port = int(ini.get('mqtt', 'port'))
    clean_session = bool(ini.get('mqtt', 'clean_session'))
    keepalive = int(ini.get('mqtt', 'keepalive'))
    username = ini.get('mqtt', 'username')
    password = ini.get('mqtt', 'password')

    db_url = ini['db']['url']
    db_user = ini['db']['user']
    db_password = ini['db']['password']

    tenant_code = ini['sys']['tenant_code']
    delete_log_path = ini['settings']['delete_log_path'] == 'true'

    topic_list = [
        (f"gateway/env/wind/{tenant_code}/+", 1),

        (f"mobile/up/general-train/{tenant_code}/+/+", 1),
        (f"mobile/up/route-train/{tenant_code}/+/+", 1),
        (f"mobile/up/original/{tenant_code}/+/+", 1),

        (f"mobile/up/record/{tenant_code}/+/+", 2),
        (f"pad/up/record/{tenant_code}/+/+", 2),

        (f"gw/client/+", 2)
    ]

    sailboat_monitor = SailboatMonitor(tenant_code=tenant_code, client_id=client_id, mqtt_host=mqtt_host, port=port,
                                       clean_session=clean_session, mqtt_username=username, mqtt_password=password,
                                       keepalive=keepalive, topic_list=topic_list,
                                       db_url=db_url, db_user=db_user, db_password=db_password,
                                       delete_log_path=delete_log_path)
    sailboat_monitor.initPath()
    sailboat_monitor.initMqtt(level_info, user_info)
